/**
 * @brief MCU 适配层统一公开接口
 * @detail 本头文件中定义了各种 MCU 统一的公开接口，用户程序应尽量只使用本头文件中的接口，以实现跨 MCU 的兼容性！
 * @author 张勇 / 2020-05-13
 */
 
#pragma once

#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>

#include "share/std_err.h"
#include "share/transceiver.h"


// forward 声明: 串口收发器
typedef struct _transceiver transceiver_t;

/// MCU 中断处理函数原型
typedef void (*mcu_isr_t)(void *cookie);


#ifdef __cplusplus
extern "C" {
#endif

// BSP 提供的函数: 设置中断请求优先级
typedef std_err_t (*func_bsp_set_irq_priorities)();

/// MCU 库初始化
std_err_t mcu_init(func_bsp_set_irq_priorities bsp_set_irq_priorities);

//
// 用于解决 MCU 的字节序问题、地址访问对齐问题的相关函数
//

/// MCU 字节对齐字节数
size_t mcu_align_bytes();

/// MCU 是否是大端在前模式
bool mcu_is_big_endian();
/// MCU 是否是小端在前模式
static inline bool mcu_is_little_endian() { return !mcu_is_big_endian(); }

// 从一个字节流中，以 大端/小端 模式读取 64/32/16 位 无符号/有符号 整数，解决字节对齐问题，同时解决字节序问题
uint64_t mcu_get_beu64(const void *stream);
uint32_t mcu_get_beu32(const void *stream);
uint16_t mcu_get_beu16(const void *stream);
uint64_t mcu_get_leu64(const void *stream);
uint32_t mcu_get_leu32(const void *stream);
uint16_t mcu_get_leu16(const void *stream);
static inline int64_t mcu_get_bes64(const void *stream) { return (int64_t) mcu_get_beu64(stream); }
static inline int32_t mcu_get_bes32(const void *stream) { return (int32_t) mcu_get_beu32(stream); }
static inline int16_t mcu_get_bes16(const void *stream) { return (int16_t) mcu_get_beu16(stream); }
static inline int64_t mcu_get_les64(const void *stream) { return (int64_t) mcu_get_leu64(stream); }
static inline int32_t mcu_get_les32(const void *stream) { return (int32_t) mcu_get_leu32(stream); }
static inline int16_t mcu_get_les16(const void *stream) { return (int16_t) mcu_get_leu16(stream); }

/// MCU 支持的指针存储空间占多少个字节
static inline size_t mcu_pointer_size() { return sizeof(void*); }
/// 从一个遵从本机字节序的字节流中，读取一个指针，解决字节对齐问题，同时解决字节序问题
void* mcu_get_pointer(const void *stream);

// MCU 字节序与网絡字节序的相互转换(ntoh: net -> mcu, hton: mcu -> net)
// 网络字节顺序采用 big endian 排序方式，如果 MCU 的字节序是 little endian 方式时，需要进行转换
uint64_t mcu_ntoh_u64(uint64_t n);
uint32_t mcu_ntoh_u32(uint32_t n);
uint16_t mcu_ntoh_u16(uint16_t n);
static inline  int64_t mcu_ntoh_s64( int64_t n) { return (int64_t) mcu_ntoh_u64((uint64_t)n); }
static inline  int32_t mcu_ntoh_s32( int32_t n) { return (int32_t) mcu_ntoh_u64((uint32_t)n); }
static inline  int16_t mcu_ntoh_s16( int16_t n) { return (int16_t) mcu_ntoh_u64((uint16_t)n); }

static inline uint64_t mcu_hton_u64(uint64_t n) { return mcu_ntoh_u64(n); }
static inline uint32_t mcu_hton_u32(uint32_t n) { return mcu_ntoh_u32(n); }
static inline uint16_t mcu_hton_u16(uint16_t n) { return mcu_ntoh_u16(n); }
static inline  int64_t mcu_hton_s64( int64_t n) { return mcu_ntoh_s64(n); }
static inline  int32_t mcu_hton_s32( int32_t n) { return mcu_ntoh_s32(n); }
static inline  int16_t mcu_hton_s16( int16_t n) { return mcu_ntoh_s16(n); }

/// 检查目标地址段是否处于 RAM 中
bool mcu_is_in_ram(uintptr_t addr, uintptr_t len);
/// 检查目标地址段是否处于 FLASH 中
bool mcu_is_in_flash(uintptr_t addr, uintptr_t len);

/// 数据块写入 FLASH (自动根据需要擦除并解决跨页写问题)
/// @param addr 目标地址，必须在 Flash 中
/// @param len 要写入的数据长度，必须在 Flash 中
/// @param data 要写入的数据
/// @return 标准错误码
std_err_t mcu_flash_write(uintptr_t addr, uint32_t len, const void *data);


//
// 与 MCU 启动相关的函数
//
/// @brief 获得启动模式
uint32_t mcu_get_boot_mode();
/// @brief 软件重启 MCU
void mcu_reboot(uint32_t reason);
/// @brief 获得重启原因
uint32_t mcu_get_reboot_reason();
/// @brief 清除系统重启原因(设置为0)
void mcu_clear_reboot_reason(void);
/// @brief 是否是非正常重启
bool mcu_is_abnormal_reboot();
/// 是否处于 JTAG/SWD 等调试状态
bool mcu_is_debugging();
/// 获取 MCU 芯片的唯一 ID
const uint8_t* mcu_get_chipid(size_t *size);
/// 获取 MCU VBAT 电压(mv)
uint16_t mcu_get_vbat(void);

/// @brief 看门狗最大定时时间(ms)
uint32_t mcu_watchdog_timeout_max();
/// @brief 初始化看门狗
/// @timeout 看门狗定时(ms)
std_err_t mcu_watchdog_init(uint32_t timeout_ms);
/// @brief 喂看门狗
void mcu_watchdog_feed(void);


//
// 与 MCU 时钟相关的函数
//
/// @brief 初始化 MCU 时钟
std_err_t mcu_clock_init();

/// @brief 获取 MCU 主频
uint32_t mcu_clock_get_sysfreq(void);
/// @brief 获取当前 SysTick 时钟计数
uint32_t mcu_clock_get_systick(void);

/// @brief 获取当前系统时钟微秒数
uint64_t mcu_clock_us_now();
/// @brief 延时us（如果有操作系统，不挂起当前任务）
void mcu_clock_us_delay(uint32_t x);

/// @brief 获取当前系统时钟计数器毫秒数（一般表示自系统启动以来的持续时间）
uint64_t mcu_clock_ms_now();
/// @brief 延时ms（如果有操作系统，会挂起当前任务，调度其它任务运行）
void mcu_clock_ms_delay(uint32_t x);

/// @brief 获得当前时间(自1970-1-1 00:00:00至当前的秒数)
uint32_t mcu_clock_get_rtcsec();
/// @brief 设置当前时间(自1970-1-1 00:00:00至当前的秒数)
std_err_t mcu_clock_set_rtcsec(uint32_t sec);
/// @breif 设置时间
std_err_t mcu_clock_rtc_set(int year, int month, int day, int hour, int minute, int second);
/// @breif 获取时间
std_err_t mcu_clock_rtc_get(int *year, int *month, int *day, int *hour, int *minute, int *second, int *weekday);

/// @brief 设置秒中断回调函数
void mcu_clock_set_rtcsec_handler(mcu_isr_t handler, void* cookie);
/// @brief 设置闹钟
std_err_t mcu_clock_set_rtc_alarm(uint32_t timestamp, mcu_isr_t handler, void* cookie);

/// forward 声明: 在 RTC 后备区中存放的参数数据结构
typedef struct _mcu_rtc_bkp mcu_rtc_bkp_t;
/// @brief RTC 后备区内存读
mcu_rtc_bkp_t* mcu_rtc_bkp_read();
/// @brief RTC 后备区内存写
std_err_t mcu_rtc_bkp_write(mcu_rtc_bkp_t *bkp);


//
// 与 MCU 中断管理相关的函数
//
/// forward 定义: MCU 的中断控制器，不同型号的 MCU 实际实现可能是不同的 !!!
/// 如对于 Xilinx A9/A53 就是 XScuGic*
typedef void* mcu_intc_t;

/// 初始化 MCU 的中断控制器
std_err_t mcu_intc_init();

/// 判断一个地址是否一个正确的程序入口地址（包括包括正确的 栈地址 和 中断向量表）
bool mcu_validate_intvect_entry(uintptr_t entry);
/// 跳转到一个中断向量中去执行
void mcu_jump_to_intvect(uintptr_t entry);
/// 依次调用所有注册的中断向量处理函数
void mcu_call_irq_handlers(int irqn);

/// 获取 MCU 的中断控制器
mcu_intc_t mcu_intc();

/// @brief 关闭所有可屏蔽中断
void mcu_disable_interrupts();
/// @brief 打开所有中断
void mcu_enable_interrupts();

/// 中断状态数据类型
typedef uintptr_t mcu_istate_t;
/// 获取全局中断开关状态
mcu_istate_t mcu_get_interrupts_state();
/// 设置全局中断开关状态
void mcu_set_interrupts_state(mcu_istate_t state);

/// 临时关闭中断，并在稍后恢复
#define mcu_pause_irqs(ist)		mcu_istate_t ist = mcu_get_interrupts_state(); mcu_disable_interrupts()
#define mcu_resume_irqs(ist)	mcu_set_interrupts_state(ist)

/// @brief 当前是否在中断中
bool mcu_is_in_isr();

/**
 * 配置中断
 * @param irqn 中断号
 * @param priority 中断优先级(0 ~ 31)，数值越小，优先级越高
 * @param trigtype 中断触发类型，对于不同类型的中断，此参数定义不同
 * @return 标准错误码
 * @attention 对于 Xilinx A53/A9 请参考 XScuGic_SetPriorityTriggerType() 参数取值范围
 */
std_err_t mcu_intc_set_priority_trigtype(mcu_intc_t intc, int32_t irqn, uint8_t  priority, uint8_t  trigtype);
std_err_t mcu_intc_get_priority_trigtype(mcu_intc_t intc, int32_t irqn, uint8_t *priority, uint8_t *trigtype);
std_err_t mcu_intc_set_priority(mcu_intc_t intc, int32_t irqn, uint8_t priority);
std_err_t mcu_intc_set_intrtype(mcu_intc_t intc, int32_t irqn, uint8_t trigtype);

/// 注册中断向量处理函数
std_err_t mcu_intc_register_handler(mcu_intc_t intc, int32_t irqn, mcu_isr_t isr, void *cookie);
/// 卸载中断向量处理函数
void mcu_intc_unregister_handler(int32_t irqn, mcu_isr_t isr);

/// 使能/禁用指定中断
std_err_t mcu_intc_enable(mcu_intc_t intc, int32_t irqn, bool enable);


//
// 与 GPIO 相关的函数
//

// forward 声明: GPIO 号
typedef enum _mcu_gpio_num mcu_gpio_num_t;

/// @brief GPIO 工作模式
typedef enum _mcu_gpio_mode {
	MCU_GPIO_MODE_NONE,     	// 未指定
	MCU_GPIO_MODE_INPUT,   		// 默认输入
	MCU_GPIO_MODE_INPUT_FLOAT,	// 不上下拉/浮空输入
	MCU_GPIO_MODE_INPUT_PU,     // 上拉输入
	MCU_GPIO_MODE_INPUT_PD,     // 下拉输入
	MCU_GPIO_MODE_OUTPUT,  		// 默认输出
	MCU_GPIO_MODE_OUTPUT_PP,    // 推挽输出，可增强驱动能力
	MCU_GPIO_MODE_OUTPUT_OD,	// 开漏输出，只能输出低电平，高电平电压由外部上拉电压决定
	MCU_GPIO_MODE_MAX
} mcu_gpio_mode_t;

/// @brief 外部中断触发类型
typedef enum _mcu_gpio_intr_type {
    MCU_GPIO_INTR_POSEDGE,	// GPIO interrupt type : rising edge
    MCU_GPIO_INTR_NEGEDGE,	// GPIO interrupt type : falling edge
    MCU_GPIO_INTR_2EDGES ,	// GPIO interrupt type : rising and falling edges
    MCU_GPIO_INTR_LVHIGH ,	// GPIO interrupt type : level-high
    MCU_GPIO_INTR_LVLOW  ,	// GPIO interrupt type : level-low
	MCU_GPIO_INTR_MAX
} mcu_gpio_intr_type_t;

/// @brief 命名的 GPIO 有以下可使用的接口函数
typedef std_err_t (*func_gpio_config_t)(mcu_gpio_mode_t mode);

typedef int8_t    (*func_gpio_read_in_t)(void);
typedef std_err_t (*func_gpio_exti_config_t)(mcu_gpio_intr_type_t trig, void *opt, mcu_isr_t isr, void *isr_cookie);
typedef int       (*func_gpio_exti_irqn_t)(void);
typedef std_err_t (*func_gpio_exti_toggle_trig_polarity_t)(void);
typedef std_err_t (*func_gpio_exti_enable_t)(void);
typedef std_err_t (*func_gpio_exti_disable_t)(void);
typedef uint32_t  (*func_gpio_exti_flag_t)(void);
typedef std_err_t (*func_gpio_exti_clear_t)(void);

typedef std_err_t (*func_gpio_write_out_t)(bool x);
typedef int8_t    (*func_gpio_read_out_t)(void);
typedef std_err_t (*func_gpio_toggle_t)(void);

typedef int8_t    (*func_gpio_bidi_read_in_t)(void);
typedef std_err_t (*func_gpio_bidi_write_out_t)(bool x);

typedef std_err_t (*func_gpio_on_t)(void);
typedef std_err_t (*func_gpio_off_t)(void);
typedef std_err_t (*func_gpio_switch_t)(bool on);
typedef bool      (*func_gpio_is_in_on_t)(void);
typedef bool      (*func_gpio_is_in_off_t)(void);
typedef bool      (*func_gpio_is_out_on_t)(void);
typedef bool      (*func_gpio_is_out_off_t)(void);

/// @brief 声明命名的 GPIO APIs(基本的)
#define GPIO_DECLARE(NAME) \
	std_err_t GPIO_##NAME##_Config(mcu_gpio_mode_t mode); \
	int8_t    GPIO_##NAME##_ReadIn(void); \
	std_err_t GPIO_##NAME##_WriteOut(bool x); \
	int8_t    GPIO_##NAME##_ReadOut(void); \
	std_err_t GPIO_##NAME##_Toggle(void); \
	std_err_t GPIO_##NAME##_EXTI_Config(mcu_gpio_intr_type_t trig, void *opt, mcu_isr_t isr, void *isr_cookie); \
	int       GPIO_##NAME##_EXTI_IRQn(void); \
	std_err_t GPIO_##NAME##_EXTI_ToggleTrigPolarity(void); \
	std_err_t GPIO_##NAME##_EXTI_Enable(void); \
	std_err_t GPIO_##NAME##_EXTI_Disable(void); \
	uint32_t  GPIO_##NAME##_EXTI_Flag(void); \
	std_err_t GPIO_##NAME##_EXTI_Clear(void);

/// @brief 声明命名的 GPIO APIs(带极性的)
#define GPIO_DECLARE_POLARITY(NAME) \
	GPIO_DECLARE(NAME) \
	std_err_t GPIO_##NAME##_On(void); \
	std_err_t GPIO_##NAME##_Off(void); \
	std_err_t GPIO_##NAME##_Switch(bool on); \
	bool      GPIO_##NAME##_IsInOn(void); \
	bool      GPIO_##NAME##_IsInOff(void); \
	bool      GPIO_##NAME##_IsOutOn(void); \
	bool      GPIO_##NAME##_IsOutOff(void);

/// @brief 声明命名的 GPIO APIs(双向I/O)
#define GPIO_DECLARE_BIDI(NAME) \
	int8_t    GPIO_##NAME##_BidiReadIn(void); \
	std_err_t GPIO_##NAME##_BidiWriteOut(bool x);


//
// 与 UART 串口相关的函数
//

// forward 声明: UART 接口数据结构
typedef struct _mcu_uart mcu_uart_t;

/// @brief 数据位定义
typedef enum {
	MCU_UART_DATABITS_7 = 0,
	MCU_UART_DATABITS_8,
	MCU_UART_DATABITS_9,
} mcu_uart_databits_t;

/// @brief 停止位定义
typedef enum {
	MCU_UART_STOPBITS_0_5 = 0,
	MCU_UART_STOPBITS_1,
	MCU_UART_STOPBITS_1_5,
	MCU_UART_STOPBITS_2,
} mcu_uart_stopbits_t;

/// @brief 奇偶校验位定义
typedef enum {
	MCU_UART_PARITY_NO = 0,
	MCU_UART_PARITY_EVEN,
	MCU_UART_PARITY_ODD,
	MCU_UART_PARITY_MAX
} mcu_uart_parity_t;

/// @brief UART 回调函数: 收到数据
typedef void (*func_uart_rxbyte_t)(mcu_uart_t *dev, void *cookie, uint8_t ch);
/// @brief UART 回调函数: 发送缓冲区空
typedef void (*func_uart_txempty_t)(mcu_uart_t *dev, void *cookie);

/// @brief 串口 API 函数类型声明
typedef std_err_t (*func_uart_putc_t)(mcu_uart_t *dev, uint8_t ch);
typedef std_err_t (*func_uart_getc_t)(mcu_uart_t *dev, uint8_t *ch);
typedef std_err_t (*func_uart_set_rxbyte_callback_t)(mcu_uart_t* dev, func_uart_rxbyte_t rxbyte_callback, void *rxbyte_cookie);
typedef std_err_t (*func_uart_set_txempty_callback_t)(mcu_uart_t* dev, func_uart_txempty_t txempty_callback, void *txempty_cookie);
typedef std_err_t (*func_uart_rx_irq_enable_t)(mcu_uart_t* dev, bool sw);
typedef std_err_t (*func_uart_tx_irq_enable_t)(mcu_uart_t* dev, bool sw);
typedef std_err_t (*func_uart_clear_rxfifo_t)(mcu_uart_t* dev);
typedef std_err_t (*func_uart_clear_txfifo_t)(mcu_uart_t* dev);
typedef std_err_t (*func_uart_send_t)(mcu_uart_t* dev, const void* data, size_t len, size_t *sentlen);
typedef std_err_t (*func_uart_sendstr_t)(mcu_uart_t* dev, const char* str);
typedef rx_ret_t  (*func_uart_receive_t)(mcu_uart_t* dev, void* buf, size_t size, size_t *len, long timeout, /*long lead,*/ long interval, const char *okFlag, rx_flag_t okFlagType, const char *failFlag, rx_flag_t failFlagType);


/// @brief 串口写一个字符
std_err_t mcu_uart_putc(mcu_uart_t *dev, uint8_t ch);
/// @brief 串口读一个字符
std_err_t mcu_uart_getc(mcu_uart_t *dev, uint8_t *ch);
/// @brief 设置 UART 回调函数: 收到数据
std_err_t mcu_uart_set_rxbyte_callback(mcu_uart_t* dev, func_uart_rxbyte_t rxbyte_callback, void *rxbyte_cookie);
/// @brief 设置 UART 回调函数: 发送缓冲区空
std_err_t mcu_uart_set_txempty_callback(mcu_uart_t* dev, func_uart_txempty_t txempty_callback, void *txempty_cookie);
/// @brief 使能/禁用 RXNE 中断
std_err_t mcu_uart_rx_irq_enable(mcu_uart_t* dev, bool sw);
/// @brief 使能/禁用 TXE 中断
std_err_t mcu_uart_tx_irq_enable(mcu_uart_t* dev, bool sw);
/// @brief 清除 RX FIFO
std_err_t mcu_uart_clear_rxfifo(mcu_uart_t* dev);
/// @brief 清除 TX FIFO
std_err_t mcu_uart_clear_txfifo(mcu_uart_t* dev);

/// @brief 发送整个数据缓冲区
/// @param sentlen [out] 实际发送的字节数
std_err_t mcu_uart_send(mcu_uart_t* dev, const void* data, size_t len, size_t *sentlen);
/// @brief 发送字符串
std_err_t mcu_uart_sendstr(mcu_uart_t* dev, const char* str);

/// @brief 串口阻塞式接收数据
/// @param dev 串口设备指针
/// @param buf 应用程序提供的接收缓冲区指针，大小由 size 指示
/// @param size 最大接收数据大小。数据缓冲区满之后，应立即返回 RX_RET_FULL
/// @param len [out] 实际接收到的数据大小，不应超过 size 大小；可以为 NULL，表示实际接收到的数据大小由其它途径获取(如字符串长度)
/// @param timeout 总超时(ms)。-1 表示永远等待。超时后，不管接收到多少数据都应返回 RX_RET_TIMEOUT
/// @param lead 前导超时(ms)，表示第一个字节到来之前等待超时时间。超时后，RX_RET_LEAD
/// @param interval 间隔超时(ms)，表示底层两个接收数据片之间最大的间隔。超时后，不管接收到多少数据都应返回 RX_RET_INTERVAL
/// @param okFlag 正常返回时的标志。具体类型由 okFlagType 决定。匹配到此标志之后，应返回 RX_RET_OK
/// @param okFlagType 表示存放在 okFlag 中的具体标志类型。@see rx_flag_t
/// @param failFlag 异常返回时的标志。具体类型由 failFlagType 决定。匹配到此标志之后，应返回 RX_RET_FAIL
/// @param failFlagType 表示存放在 failFlag 中的具体标志类型。@see rx_flag_t
/// @return 一个 rx_ret_t 型整数，表示接收结束的原因。@see rx_ret_t
rx_ret_t mcu_uart_receive(mcu_uart_t* dev, void* buf, size_t size, size_t *len, long timeout, /*long lead,*/ long interval, const char *okFlag, rx_flag_t okFlagType, const char *failFlag, rx_flag_t failFlagType);

/// @brief 设置用于标准 I/O 的 UART 端口
void mcu_stduart_set(mcu_uart_t* dev);
/// @brief 获取用于标准 I/O 的 UART 端口
mcu_uart_t* mcu_stduart_get();

// forward 声明
typedef struct _console console_t;

// forward 声明
typedef struct _ymodem ymodem_t;
/// @brief 做为 Y-Modem 开始监听
std_err_t mcu_uart_listen_for_ymodem(mcu_uart_t* dev, ymodem_t* modem);

/// @brief 打开以 UART 做为物理层的 transceiver_t
std_err_t mcu_uart_trx_open(const char* addr, transceiver_t** trx);

/// @brief 声明命名的 UART APIs
#define UART_DECLARE(NAME) \
	std_err_t UART_##NAME##_Init(uint32_t baudrate, mcu_uart_databits_t databits, mcu_uart_stopbits_t stopbits, mcu_uart_parity_t parity); \
	extern mcu_uart_t*    UART_##NAME; \
	extern transceiver_t* NAME##_TRX;


//
// 与 SPI 接口相关的函数
//

// forward 声明: SPI 接口数据结构
typedef struct _mcu_spi mcu_spi_t;

/// @brief 设置 SPI 接口波特率
std_err_t mcu_spi_set_baudrate(mcu_spi_t *dev, uint32_t baudrate);
/// @brief 通过 SPI 接口读写一块数据
std_err_t mcu_spi_trx(mcu_spi_t *dev, const void *tx, void *rx, size_t len, size_t *trxlen);
/// @brief 通过 SPI 接口写一块数据
static inline std_err_t mcu_spi_tx(mcu_spi_t *dev, const void *tx, size_t len, size_t *txlen) { return mcu_spi_trx(dev, tx, NULL, len, txlen); }
/// @brief 通过 SPI 接口读一块数据
static inline std_err_t mcu_spi_rx(mcu_spi_t *dev, void *rx, size_t len, size_t *rxlen) { return mcu_spi_trx(dev, NULL, rx, len, rxlen); }
/// @brief 通过 SPI 接口读写一个字节
static inline uint8_t mcu_spi_trx1(mcu_spi_t *dev, uint8_t tx) { uint8_t rx; mcu_spi_trx(dev, &tx , &rx , 1, NULL); return rx; }
/// @brief 通过 SPI 接口读一个字节
static inline uint8_t mcu_spi_rx1(mcu_spi_t *dev)              { uint8_t rx; mcu_spi_trx(dev, NULL, &rx , 1, NULL); return rx; }
/// @brief 通过 SPI 接口写一个字节
static inline std_err_t mcu_spi_tx1(mcu_spi_t *dev, uint8_t tx) {     return mcu_spi_trx(dev, &tx , NULL, 1, NULL); }

#define SPI_DECLARE(NAME) \
	extern mcu_spi_t* SPI_##NAME;


//
// 与 I2C 接口相关的函数
//

// forward 声明: I2C 接口数据结构
typedef struct _mcu_i2c mcu_i2c_t;

/// @brief I2C 接口设置为 debug 模式: 在此模式下，进入调试断点时，I2C 不再超时计数
std_err_t mcu_i2c_debug(mcu_i2c_t* dev, bool enable);

/// @brief I2C 接口复位
std_err_t mcu_i2c_reset(mcu_i2c_t* dev);

/// @brief I2C 接口做为 master 阻塞式发/收数据: 产生start条件 -> 发送数据 -> 接收数据 -> 产生stop条件
/// @param start 在发送数据之前，是否需要先 产生start条件
/// @param stop 通信结束之后，是否需要 产生stop条件
/// @param txlen, txdata (必需)要发送的数据长度和地址。一般第一个字节是目标设备的 I2C 监听地址，这些数据是在产生start条件后，立即发送的，并且要求对端对发送的每个字节给出 ACK，否则认为发送失败。
/// @param txlen, txdata (必需)要发送的数据长度和地址。一般第一个字节是目标设备的 I2C 监听地址，这些数据是在产生start条件后，立即发送的，并且要求对端对发送的每个字节给出 ACK，否则认为发送失败。
/// @return 标准错误码
std_err_t mcu_i2c_master_trx(mcu_i2c_t* dev, bool start, bool stop, uint32_t txlen, const void* txdata, size_t rxlen, void* rxdata);


// I2C slave 模式下监听的接口地址: 具体含义由具体的芯片型号决定
typedef uint32_t mcu_i2c_slave_addr_t;
/// @brief I2C 接口接收到数据时的回调函数
typedef void (*mcu_i2c_rxdata_handler_t)(mcu_i2c_t* dev, void* cookie, uint32_t len, void* data);
/// @brief I2C 接口发送完成时的回调函数
typedef void (*mcu_i2c_txempty_handler_t)(mcu_i2c_t* dev, void* cookie);
/// @brief I2C 接口中断处理函数
void mcu_i2c_irq_handler(mcu_i2c_t* dev);
/// @brief I2C 接口开始监听
std_err_t mcu_i2c_slave_start_listen(mcu_i2c_t* dev);
/// @brief I2C 接口停止监听
std_err_t mcu_i2c_slave_stop_listen(mcu_i2c_t* dev);
/// @brief I2C 接口使用初始化时提供的接口硬件地址
/// @return 实际发送的数据长度
std_err_t mcu_i2c_slave_send(mcu_i2c_t* dev, const void* data, uint32_t len, size_t *sentlen);


/// @brief 用于声明命名的 I2C 总线的宏
#define I2CBUS_DECLARE(NAME) \
	extern mcu_i2c_t* I2C_##NAME;

#define I2CDEV_DECLARE(NAME) \
	extern mcu_i2c_t* NAME##_I2C; \
	extern uint8_t    NAME##_Addr;
	

//
// 与 ADC 接口相关的函数
//

// forward 声明: ADC 接口数据结构
typedef struct _mcu_adc mcu_adc_t;

/// @brief 获取 ADC 通道数量
size_t mcu_adc_chcount(mcu_adc_t* dev);
/// @brief 获取 ADC 最大输出值
size_t mcu_adc_max(mcu_adc_t* dev);
/// @brief 获取 ADC 采样精度(每个采样点多少位有效)
size_t mcu_adc_bits(mcu_adc_t* dev);
/// @brief 获取 ADC 采样精度(每个采样点占多少字节)
size_t mcu_adc_bytes(mcu_adc_t* dev);
/// @brief 获取 ADC 最大采样速率
size_t mcu_adc_samplerate_max(mcu_adc_t* dev);

/// @brief 初始化 ADC 通道
std_err_t mcu_adc_init(mcu_adc_t *dev, uint32_t chmask);

/// @brief 获取 ADC 参考电压(单位: V)
float mcu_adc_vref(mcu_adc_t* dev);

/// @brief 读取一次 ADC 采样值
/// @param chidx 通道编号
/// @return ADC 采样值
uint32_t mcu_adc_read1(mcu_adc_t* dev, uint32_t chidx);

/// @brief 开始 ADC 采样循环
/// @param dev 使用的 ADC 控制器
/// @param chmask 参与采样的通道掩码。每一位表示一个通道，掩码为 1 的通道同步启动采样。不应超出 mcu_adc_init() 时 chmask 的范围
/// @param sample_rate 采样率(点/秒)
/// @param samples 采样点数据缓冲区，第一维表示采样点(字节数 = sample_count * mcu_adc_bytes())，第二维表示通道
/// @param sample_count 每通道采样点数量
/// @param looping 是否循环采样。=true 时，本函数立即返回，由硬件（如DMA）进行循环采样，直至调用 mcu_adc_stop()；否则只采样一次，采样完成后返回
std_err_t mcu_adc_start(mcu_adc_t* dev, uint32_t chmask, uint8_t sample_rate, void* samples[], uint32_t sample_count, bool looping);
/// @brief 停止 ADC 采样
/// @param chmask 通道掩码。每一位表示一个通道，掩码为 1 的通道同步停止采样
std_err_t mcu_adc_stop(mcu_adc_t* dev);
/// @brief 获取 ADC 采样循环当前采样点在缓冲区中的偏移(第几个点)
uint32_t  mcu_adc_curr(mcu_adc_t* dev);


//
// 与 DAC 接口相关的函数
//

// forward declares
// DAC 控制器数据结构
typedef struct _mcu_dac mcu_dac_t;

/// @brief 获取 DAC 通道数量
size_t mcu_dac_chcount(mcu_dac_t* dev);
/// @brief 获取 DAC 最大输出值
size_t mcu_dac_max(mcu_dac_t* dev);
/// @brief 获取 DAC 采样精度(每个采样点多少位有效)
size_t mcu_dac_bits(mcu_dac_t* dev);
/// @brief 获取 DAC 采样精度(每个采样点占多少字节)
size_t mcu_dac_bytes(mcu_dac_t* dev);

/// @brief 设置 DAC 固定输出值
/// @param chmask 通道掩码。每一位表示一个通道，掩码为 1 的通道同步启动输出
/// @param val 输出值
std_err_t mcu_dac_write(mcu_dac_t* dev, uint32_t chmask, int32_t val);

/// @brief 开始 DAC 采样输出
/// @param chmask 通道掩码。每一位表示一个通道，掩码为 1 的通道同步启动输出
std_err_t mcu_dac_start(mcu_dac_t* dev, uint32_t chmask);
/// @brief 停止 DAC 采样输出
/// @param chmask 通道掩码。每一位表示一个通道，掩码为 1 的通道同步停止输出
std_err_t mcu_dac_stop(mcu_dac_t* dev, uint32_t chmask);

/// @brief 开始 DAC 采样循环
/// @param dev 使用的 DAC 控制器
/// @param chmask 通道掩码。每一位表示一个通道，掩码为 1 的通道同步启动输出
/// @param sample_rate 采样率(点/秒)
/// @param samples 采样点数据缓冲区，第一维表示采样点(字节数 = sample_count * mcu_dac_bytes())，第二维表示通道
/// @param sample_count 每通道采样点数量
/// @param looping 是否循环采样。=true 时，本函数立即返回，由硬件（如DMA）进行循环采样，直至调用 mcu_dac_stop()；否则只采样一次，采样完成后返回
std_err_t mcu_dac_sample(mcu_dac_t* dev, uint32_t chmask, uint8_t sample_rate, void* samples[], uint32_t sample_count, bool looping);
/// @brief 获取 DAC 采样循环当前采样点在缓冲区中的偏移(第几个点)
uint32_t  mcu_dac_curr(mcu_dac_t* dev);

/// @brief DAC 自动波形生成类型
typedef enum _mcu_dac_awg_type {
	MCU_DAC_AWG_TYPE_NOISE = 0,	// 噪声信号
	MCU_DAC_AWG_TYPE_SQURE,		// 方波, amplitude, duty 参数有效
	MCU_DAC_AWG_TYPE_TRIANGLE,	// 三角波, amplitude, duty 参数有效
	MCU_DAC_AWG_TYPE_SIN,		// 正弦波, amplitude, freq 参数有效
	MCU_DAC_AWG_TYPE_MAX
} mcu_dac_awg_type_t;

/// @brief 开始 DAC 波形生成循环: 本函数立即返回，由硬件（如DMA）进行循环采样，直至调用 mcu_dac_awg_stop()
/// @param sample_rate 采样率(点/秒)
/// @param amplitude 信号幅度(有效位数不应大于 mcu_dac_bits())
/// @param freq 信号频率(单位Hz)
/// @param duty 信号占空比(单位0.01%): 对方波而言是高电平占空比；对三角波而言是左半边占空比
std_err_t mcu_dac_awg_start(mcu_dac_t* dev, uint32_t sample_rate, int32_t amplitude, int32_t freq, int32_t duty);
/// @brief 停止 DAC 波形生成循环
std_err_t mcu_dac_awg_stop(mcu_dac_t* dev);


//
// 与 Timer 相关的接口
//

// forward 声明: Timer 接口数据结构
typedef struct _mcu_timer mcu_timer_t;
// forward 声明: Timer (捕获)输入/(比较)输出 通道 数据结构
typedef struct _mcu_timer_ch mcu_timer_ch_t;
// forward 声明: 音符音乐
typedef struct stNote Note;

// Timer 的时钟来源
typedef enum _mcu_timer_clksrc {
    MCU_TIMER_CLKSRC_INT = 0,   // 内部时钟: clksrc_opt 参数未用
    MCU_TIMER_CLKSRC_CH,        // Timer 的(捕获)输入通道: (mcu_timer_ch_t*)clksrc_opt
    MCU_TIMER_CLKSRC_ETR,       // Timer 的 ETR (外部触发)输入 GPIO 引脚: (mcu_gpio_num_t)clksrc_opt
    MCU_TIMER_CLKSRC_ITR,       // Timer 的 ITR (内部触发)信号(来自另一个 Timer 的内部触发输出信号): (mcu_timer_t*)clksrc_opt
    MCU_TIMER_CLKSRC_MAX
} mcu_timer_clksrc_t;

/// @brief 声明命名的 Timer
#define TIMER_DECLARE(NAME) \
    extern mcu_timer_t *NAME; \
    extern std_err_t NAME##_Init(mcu_timer_clksrc_t clksrc, void *clksrc_opt, uint32_t freq_main, uint32_t freq_mod); \
    extern std_err_t NAME##_SetFreqMod(uint32_t freq_mod); \
    extern std_err_t NAME##_Start(); \
    extern std_err_t NAME##_Stop();
    
/// @brief 声明命名的 Timer 通道
#define TIMERCH_DECLARE(CHNAME) \
    extern mcu_timer_ch_t *CHNAME; \
    std_err_t CHNAME##_Init(); \
	std_err_t CHNAME##_OcSetDuty(uint32_t duty); \
	uint32_t  CHNAME##_OcGetDuty(void);          \
	std_err_t CHNAME##_OcStart(unios_us_t us);   \
	std_err_t CHNAME##_OcStop(void);             \
    std_err_t CHNAME##_OcPlayNotes(const Note *notes, uint32_t speed);
    

//
// 与 NAND 相关的接口
//

// forward declares
typedef struct _mcu_nand mcu_nand_t;

// APIs for FatFS diskio
uint8_t mcu_nand_diskio_initialize(mcu_nand_t *pdrv);
uint8_t mcu_nand_diskio_status(mcu_nand_t *pdrv);
int mcu_nand_diskio_read(mcu_nand_t *pdrv, uint8_t *buff, uint32_t sector, uint32_t count);
int mcu_nand_diskio_write(mcu_nand_t *pdrv, const uint8_t *buff, uint32_t sector, uint32_t count);
int mcu_nand_diskio_ioctl(mcu_nand_t *pdrv, uint8_t cmd, void *buff);


//
// 与 SDIO/SD卡 相关的接口
//

// forward declares
typedef struct _mcu_sdio mcu_sdio_t;

///@brief 初始化 SD 卡
///@return 标准错误码。其中 STD_ERR_NO_RESOURCES 表示无 SD 卡插入。
std_err_t mcu_sdio_init(mcu_sdio_t *sdio);

///@brief 检测 SD 卡是否已插入，如果插入则获取 SD 卡信息
///@return 标准错误码。其中 STD_ERR_NO_RESOURCES 表示无 SD 卡插入。
std_err_t mcu_sdio_check_card(mcu_sdio_t *sdio);

///@brief 获得 SD 卡容量(字节数)
uint64_t mcu_sdio_size(mcu_sdio_t *sdio);

/**
 * @brief  读 SD 卡
 * @param  pbuf  : 数据缓存区
 * @param  saddr : 扇区地址(第几个扇区)
 * @param  cnt   : 扇区个数
 * @retval 0, 正常;  其他, 错误代码
 */
std_err_t mcu_sdio_read(mcu_sdio_t *sdio, uint8_t *pbuf, uint32_t saddr, uint32_t cnt);

/**
 * @brief  写 SD 卡
 * @param  pbuf  : 数据缓存区
 * @param  saddr : 扇区地址(第几个扇区)
 * @param  cnt   : 扇区个数
 * @retval 0, 正常;  其他, 错误代码
 */
std_err_t mcu_sdio_write(mcu_sdio_t *sdio, uint8_t *pbuf, uint32_t saddr, uint32_t cnt);


//
// 与 SDIO/MMC卡 相关的接口
//

// forward declares
typedef struct _mcu_sdmmc mcu_sdmmc_t;

///@brief 初始化 MMC 卡
///@return 标准错误码。其中 STD_ERR_NO_RESOURCES 表示无 MMC 卡插入。
std_err_t mcu_sdmmc_init(mcu_sdmmc_t *sdmmc);

///@brief 检测 MMC 卡是否已插入，如果插入则获取 MMC 卡信息
///@return 标准错误码。其中 STD_ERR_NO_RESOURCES 表示无 MMC 卡插入。
std_err_t mcu_sdmmc_check_card(mcu_sdmmc_t *sdmmc);

///@brief 获得 MMC 卡容量(字节数)
uint64_t mcu_sdmmc_size(mcu_sdmmc_t *sdmmc);

/**
 * @brief  读 MMC 卡
 * @param  pbuf  : 数据缓存区
 * @param  saddr : 扇区地址(第几个扇区)
 * @param  cnt   : 扇区个数
 * @retval 0, 正常;  其他, 错误代码
 */
std_err_t mcu_sdmmc_read(mcu_sdmmc_t *sdmmc, uint8_t *pbuf, uint32_t saddr, uint32_t cnt);

/**
 * @brief  写 MMC 卡
 * @param  pbuf  : 数据缓存区
 * @param  saddr : 扇区地址(第几个扇区)
 * @param  cnt   : 扇区个数
 * @retval 0, 正常;  其他, 错误代码
 */
std_err_t mcu_sdmmc_write(mcu_sdmmc_t *sdmmc, uint8_t *pbuf, uint32_t saddr, uint32_t cnt);


//
// 与外挂 SDRAM 相关的接口
//

// forward declares
typedef struct _mcu_sdram mcu_sdram_t;


#ifdef __cplusplus
}
#endif
